package com.ly.mp.dal.comm.config;

import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import javax.sql.DataSource;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import com.alibaba.druid.pool.DruidDataSource;
import com.ly.mp.dal.comm.config.condition.TransactionPolicyNormal;
import com.ly.mp.dal.comm.jdbc.DynamicDataSource;

@Configuration
@Conditional({TransactionPolicyNormal.class})
@EnableTransactionManagement(proxyTargetClass = true)
@ImportResource({"classpath:ly-mp-readdb.xml"})
public class MpDataBasesNormalConfiguration {
    public Map<Object, Object> getReadDbSource(String master, BaseDataSourceInfo baseInfo, DataBaseReadSet dbReadSet) {
        Map<Object, Object> targetDataSources = new HashMap();
        Map<String, List<DataSourceInfo>> readMaps = dbReadSet.getReadDbInfos();
        List<DataSourceInfo> reads = (List) readMaps.get(master);
        if (reads == null) {
            return targetDataSources;
        } else {
            int i = 0;

            DruidDataSource druidDataSource;
            for (Iterator var8 = reads.iterator(); var8.hasNext(); targetDataSources.put("default_readDBKey#" + i++,
                    druidDataSource)) {
                DataSourceInfo readInfo = (DataSourceInfo) var8.next();
                druidDataSource = new DruidDataSource();
                BeanUtils.copyProperties(baseInfo, druidDataSource);
                BeanUtils.copyProperties(readInfo, druidDataSource);
                if (readInfo.getUrl().contains("oracle")) {
                    druidDataSource.setValidationQuery("select 1 from dual");
                } else {
                    druidDataSource.setValidationQuery("select 1");
                }
            }

            return targetDataSources;
        }
    }

    @Bean(name = {"writeDataSource"}, initMethod = "init", destroyMethod = "close")
    public DruidDataSource druidDataSource(BaseDataSourceInfo baseInfo, DataSourceInfo dbInfo) {
        DruidDataSource druidDataSource = new DruidDataSource();
        BeanUtils.copyProperties(baseInfo, druidDataSource);
        BeanUtils.copyProperties(dbInfo, druidDataSource);
        if (dbInfo.getUrl().contains("oracle")) {
            druidDataSource.setValidationQuery("select 1 from dual");
        } else {
            druidDataSource.setValidationQuery("select 1");
        }

        return druidDataSource;
    }

    /**
     * 写库固定 default_writeDBKey
     * 读库编号 default_readDBKey#i
     * @see com.ly.mp.dal.comm.jdbc.ReadWriteDataSourceProcessor.determineReadOrWriteDB
     */
    @Bean(name = {"dataSource"})
    DataSource dynamicDataSource(DruidDataSource druidDataSource, BaseDataSourceInfo baseInfo, DataBaseReadSet dbReadSet) {
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put("default_writeDBKey", druidDataSource);
        DynamicDataSource dataSource = new DynamicDataSource();
        dataSource.setDefaultTargetDataSource(druidDataSource);
        dbReadSet.initReadDB(druidDataSource.getName());
        dataSource.setReadDBSize(dbReadSet.getReadDbSizes(druidDataSource.getName()));
        targetDataSources.putAll(this.getReadDbSource("default", baseInfo, dbReadSet));
        dataSource.setTargetDataSources(targetDataSources);
        return dataSource;
    }

    @Bean
    public PlatformTransactionManager transactionManager(DynamicDataSource dataSource) {
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
        transactionManager.setDataSource(dataSource);
        return transactionManager;
    }
    
    @ConditionalOnProperty("read.mp.jdbc.transactionPolicy.permit")
    @Configuration
    public static class TransactionPolicyPermit implements ApplicationListener<ContextRefreshedEvent> {
        private Logger log = LoggerFactory.getLogger(TransactionPolicyPermit.class);

        @Override
        public void onApplicationEvent(ContextRefreshedEvent event) {
            log.info("这里名称有问题, 要处理下, 这里是测试代码, 就不动了!");
            ApplicationContext applicationContext = event.getApplicationContext();
            DynamicDataSource bean = applicationContext.getBean(DynamicDataSource.class);
            Map<Object, DataSource> resolvedDataSources = bean.getResolvedDataSources();
            Set<Entry<Object,DataSource>> entrySet = resolvedDataSources.entrySet();
            for (Entry<Object, DataSource> entry : entrySet) {
                String key = (String) entry.getKey();
                if (StringUtils.contains(key, "readDBKey")) {
                    DataSource value = entry.getValue();
                    BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(DataSourceTransactionManager.class);
                    beanDefinitionBuilder.addPropertyValue("dataSource", value);
                    ((BeanDefinitionRegistry) applicationContext).registerBeanDefinition("transactionManagerTwo", beanDefinitionBuilder.getBeanDefinition());
                }
            }
            
        }
    }
}